/*
 * Socket utility common routines file.
 */

/* Includes */
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<fcntl.h>
#include<netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <sys/wait.h>
#include <sys/socket.h>   // For Socket Functions
#include <string.h>       // FOr String FUnctions
#include <pthread.h>      // For Threading FUnctions
#include <errno.h>        // For file system errors
#include <netinet/in.h>   // For tcp procol relatd operations.
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <stdarg.h>

#include "sock.h"
#include "util.h"

/**
 * open_server_socket
 *
 **/
int
open_server_socket(char *ip, int port, int max_q_len)
{
    int sock_fd = -1, isSet = 1;
    struct sockaddr_in sAddress;

    /* ToDo (Hiral) : please add support for ip address..
       from server.c get env. parameter for server ip and pass to
       this routine.. use gethostbyname & inet_aton to conver to
       int_address struct
    */


    memset((void *)&sAddress, 0, sizeof(struct sockaddr_in));
    sAddress.sin_family = AF_INET;
    sAddress.sin_addr.s_addr = htonl(INADDR_ANY);
    sAddress.sin_port = htons(port);

    // Create socket
    LOG(DEBUG, "Creating socket with port :%d", port);
    sock_fd = socket(PF_INET, SOCK_STREAM, 0);
    if (sock_fd < 0) {
        LOG(CRITICAL, "%d socket call failed", errno);
        errexit("\nCouldn't create socket: %s", strerror(errno));
    }

    /* ToDo: (Hiral) -- apply other socket options that can be configured from env. params.
       eg. SNDTIMEO, RCVTIMEO - Note this is common routine, so you might want to add it as
       parameter to this routine and then use it here.. use '|' (bitwise or) operation
       to combine more than one flags.
    */
    if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &isSet, sizeof(int)) == -1)
    {
        errexit("\nCouldn't set setsockopt");
    }

    //Bind socket
    LOG(DEBUG, "Binding socket");
    if (bind(sock_fd, (const struct sockaddr*)&sAddress, sizeof(struct sockaddr_in)) < 0) {
        close(sock_fd);
        errexit("\nFailed to bind the socket, error: %d", errno);
    }

    //Listen
    LOG(DEBUG, "Listining on socket");
    if (listen(sock_fd, max_q_len) < 0) {
        errexit("\nListen failed, error: %d", errno);
    }

    return sock_fd;
}


/**
 * Read line from socket
 * Very slow... may not use it anywhere.. testing purposes only.
 **/
int
read_line(int fd, char* buffer, size_t max_size)
{
    size_t nleft;
    size_t nread;
    char* pos;

    pos = buffer;
    nleft = max_size - 1;

    while(nleft > 0){
        *pos = '\0';
        if ((nread = read(fd, pos, 1)) < 0){
            //Error while reading socket.
            if (errno == EINTR)
            {
                nread = 0; // call read again.
            }else{
                LOG(ERROR, "%d while reading socket: %d", errno, fd);
                return -1;
            }
        }//else{//Read data successfully.

        if (0 == nread){ //Socket closed by peer.
        	LOG(ERROR, "socket closed by peer");
            return -1;
        }

        if ('\n' == *pos){ //newline encountered.
            if (*(pos-1) == '\r'){
                pos--; //skip /r/n
                nleft++;
            }
            break;
        }

        if ('\0' == *pos ){
            break;
        }

        //LOG(DEBUG, "pos: %c", *pos);

        nleft--;
        pos++;
    }

    //Null terminate for string safe operations.
    *pos = '\0';
    //LOG(DEBUG, "read line : %s", buffer);
    return (pos - buffer);
}


/**
 * Read line from socket
 **/
int
read_buffer(int fd, char* buffer, size_t max_size)
{
    size_t nleft;
    size_t nread;
    char* pos;

    pos = buffer;
    nleft = max_size - 1 ;

    while(nleft > 0 ){
        if ((nread = read(fd, pos, nleft)) < 0){
            //Error while reading socket.
            if (errno == EINTR)
            {
                nread = 0; //continue reading.. retry.
            }else{
                LOG(ERROR, "%d while reading fd: %d", errno, fd);
                return -1;
            }
        }//else{//Read data successfully.

        if (nread == 0){ //Socket closed by peer.
        	LOG (ERROR, "Connection closed by peer");
            return -1;
        }

        nleft -= nread;
        pos += nread;

    }

    //Null terminate for string safe operations.
    *pos = '\0';
    return (pos - buffer);
}

/**
 * Write buffer to socket
 */
int
write_buffer(int fd, const char* buffer, size_t size)
{
    size_t nleft;
    size_t nwritten;
    const char *pos;

    pos = buffer;
    nleft = size;
    while(nleft > 0){
        if ((nwritten = write(fd, pos, nleft)) <= 0){
            if (nwritten < 0 && errno == EINTR){
                nwritten = 0; // continue writing retry.
            }else{
                LOG(ERROR, "%d while reading fd %d", errno, fd);
                return -1;
            }
        }//else wrote data successfully.

        nleft -= nwritten;
        pos += nwritten;
    }

    return size;
}

